/* $Id:item.cc $ */
/*
   Copyright (C) 2007 by Jurjen Stellingwerff <jurjen@stwerff.xs4all.nl>
   Part of the Moros Project http://moros.sourceforge.net/

   This program is free software; you can redistribute it and/or modify
   it under the terms of the GNU General Public License version 3.
   This program is distributed in the hope that it will be useful,
   but WITHOUT ANY WARRANTY.

   See the COPYING file for more details.
*/

#define DEBUG_ROUTINE "item"

#include <sstream>
#include <fstream>
#include "../general/util.h"
#include "../general/log.h"
#include "../general/channel.h"
#include "item.h"
#include "skill.h"
#include "special.h"
#include "skill.h"
#include "item.h"
#include "action.h"
#include "../general/general.h"

item::item() {
    name="";
    nr=0;
    description="";
    status=0;
    type=-1;
    weight=0;
    skill=0;
    special=0;
    usage=map<int,class action_level>();
    maxUsage=0;
    creation=map<string,class action_skill>();
    material=map<string,class material>();
    bonus=map<int,class action_bonus>();
    maxBonus=0;
    spell=map<string,class item_spell>();
}

// Field : name

const string item::getName() const { return name; }
void item::setName(const string v) { name=v; }

// Field : nr

const int item::getNr() const { return nr; }
void item::setNr(const int v) { nr=v; }
int item::operator<(const class item &ck) const { return (this->nr < ck.nr)?1:0; }

// Field : description

const string item::getDescription() const { return description; }
void item::setDescription(const string v) { description=v; }

// Field : status

const int item::getStatus() const { return status; }
void item::setStatus(const int v) { status=v; }

// Field : type

const string itemType[]={"tool","cutting_weapon","blunt_weapon","impaling_weapon","transport","building","good"};
const int item::getType() const { return type; }
const string item::showType() const {
    if (type<0) return "";
    else return itemType[type];
}
void item::setType(const int  v) {
    if (v<0 || (unsigned int)v >= sizeof itemType/sizeof itemType[0]) type=-1;
    else type=v;
}
void item::setType(const string v) {
    type=-1;
    loopArray(itemType, tp, string) {
        if (v==*tp) {type=tp_i; break; }
    }
    if (type==-1) dbg_state("Enumerate item.type %s has unknown '%s'", getName().c_str(), v.c_str());
}

// Field : weight

const int item::getWeight() const { return weight; }
void item::setWeight(const int v) { weight=v; }

// Field : skill

class skill& item::getSkill() const { if (skill==0) throw nullpointer(); return *skill; }
void item::setSkill(class skill &v) { skill=&v; }

// Field : special

class special& item::getSpecial() const { if (special==0) throw nullpointer(); return *special; }
void item::setSpecial(class special &v) { special=&v; }

// Field : usage

class action_level& item::getUsage(const int v) { return usage[v]; }
map<int,class action_level>& item::listUsage() { return usage; }
void item::setUsage(class action_level &v) {
    if (v.getNr()==0) v.setNr(++maxUsage);
    usage[v.getNr()]=v;
}
void item::delUsage(const int v) { usage.erase(v); }
const bool item::isUsage(const int v) const { return usage.count(v)>0; }

// Field : creation

class action_skill& item::getCreation(const string v) { return creation[v]; }
map<string,class action_skill>& item::listCreation() { return creation; }
void item::setCreation(class action_skill &v) {
    creation[v.getFirst_skill().getName()]=v;
}
void item::delCreation(const string v) { creation.erase(v); }
const bool item::isCreation(const string v) const { return creation.count(v)>0; }

// Field : material

class material& item::getMaterial(const string v) { return material[v]; }
list<class material>& item::listMaterial() {
    static list<class material> res;
    res.clear();
    loopMap(material,v,class material) {
        res.push_back(*v);
    }
    res.sort();
    return res;
}
void item::setMaterial(class material &v) {
    material[v.getItem().getName()]=v;
}
void item::delMaterial(const string v) { material.erase(v); }
const bool item::isMaterial(const string v) const { return material.count(v)>0; }

// Field : bonus

class action_bonus& item::getBonus(const int v) { return bonus[v]; }
map<int,class action_bonus>& item::listBonus() { return bonus; }
void item::setBonus(class action_bonus &v) {
    if (v.getNr()==0) v.setNr(++maxBonus);
    bonus[v.getNr()]=v;
}
void item::delBonus(const int v) { bonus.erase(v); }
const bool item::isBonus(const int v) const { return bonus.count(v)>0; }

// Field : spell

class item_spell& item::getSpell(const string v) { return spell[v]; }
map<string,class item_spell>& item::listSpell() { return spell; }
void item::setSpell(class item_spell &v) {
    spell[v.getSpell().getName()]=v;
}
void item::delSpell(const string v) { spell.erase(v); }
const bool item::isSpell(const string v) const { return spell.count(v)>0; }

void item::save(class channel &ch) {
    ch.write("item", "");
    ch.write("n", getName());
    ch.write("nr", getNr());
    ch.write("d", getDescription());
    ch.write("s", getStatus());
    ch.write("t", getType());
    ch.write("w", getWeight());
    ch.write("sk", getSkill().getName());
    ch.write("sp", getSpecial().getName());
    loopItemUsage(this, s_usage) {
        s_usage->save(ch);
    }
    loopItemCreation(this, s_creation) {
        s_creation->save(ch);
    }
    loopItemMaterial(this, s_material) {
        s_material->save(ch);
    }
    loopItemBonus(this, s_bonus) {
        s_bonus->save(ch);
    }
    loopItemSpell(this, s_spell) {
        s_spell->save(ch);
    }
}

void item::load(class channel &ch) {
    if (ch.key=="n" || ch.key=="name") { setName(ch.value); ch.next(); }
    if (ch.key=="nr" || ch.key=="nr") { setNr(atoi(ch.value.c_str())); ch.next(); }
    if (ch.key=="d" || ch.key=="description") { setDescription(ch.value); ch.next(); }
    if (ch.key=="s" || ch.key=="status") { setStatus(atoi(ch.value.c_str())); ch.next(); }
    if (ch.key=="t" || ch.key=="type") { setType(ch.value); ch.next(); }
    if (ch.key=="w" || ch.key=="weight") { setWeight(atoi(ch.value.c_str())); ch.next(); }
    if (ch.key=="sk" || ch.key=="skill") {
        if (!skill_repo.isSkill(ch.value)) {
            dbg_state("link unknown skill %s", ch.value.c_str());
            class skill rec;
            rec.setName(ch.value);
            skill_repo.setSkill(rec);
        }
        setSkill(skill_repo.getSkill(ch.value));
        ch.next();
    }
    if (ch.key=="sp" || ch.key=="special") {
        if (!special_repo.isSpecial(ch.value)) {
            dbg_state("link unknown special %s", ch.value.c_str());
            class special rec;
            rec.setName(ch.value);
            special_repo.setSpecial(rec);
        }
        setSpecial(special_repo.getSpecial(ch.value));
        ch.next();
    }
    while (ch.key=="action_level" || ch.key=="usage") {
        ch.next();
        class action_level rec;
        rec.load(ch);
        setUsage(rec);
    }
    while (ch.key=="action_skill" || ch.key=="creation") {
        ch.next();
        class action_skill rec;
        rec.load(ch);
        setCreation(rec);
    }
    while (ch.key=="material" || ch.key=="material") {
        ch.next();
        class material rec;
        rec.load(ch);
        setMaterial(rec);
    }
    while (ch.key=="action_bonus" || ch.key=="bonus") {
        ch.next();
        class action_bonus rec;
        rec.load(ch);
        setBonus(rec);
    }
    while (ch.key=="item_spell" || ch.key=="spell") {
        ch.next();
        class item_spell rec;
        rec.load(ch);
        setSpell(rec);
    }
}

void item::xml(ofstream &f, int l, int r) {
    f << ncpy(l, "  ") << "<item name=\"" << xml_encode(getName()) <<
    "\" nr=\"" << getNr() <<
    "\" description=\"" << xml_encode(getDescription()) <<
    "\" status=\"" << getStatus() <<
    "\" type=\"" << showType() <<
    "\" weight=\"" << getWeight() <<
    "\">" << endl;
    if (r<2) try {
        getSkill().xml(f,l+1,r+1);
        getSpecial().xml(f,l+1,r+1);
    } catch (nullpointer n) {}
    if (r==0) {
        loopItemUsage(this, s_usage) {
            s_usage->xml(f,l+1);
        }
        loopItemCreation(this, s_creation) {
            s_creation->xml(f,l+1);
        }
        loopItemMaterial(this, s_material) {
            s_material->xml(f,l+1);
        }
        loopItemBonus(this, s_bonus) {
            s_bonus->xml(f,l+1);
        }
        loopItemSpell(this, s_spell) {
            s_spell->xml(f,l+1);
        }
    }
    f << ncpy(l, "  ") << "</item>" << endl;
}

// Repository

itemRepository item_repo;

itemRepository::itemRepository() { max_nr=0; }
class item& itemRepository::getItem(const string v) { return items[v]; }
void itemRepository::setItem(class item& v) {
    if (v.getNr()==0) v.setNr(++max_nr);
    if (v.getName()=="") throw unnamed();
    items[v.getName()]=v;
}
list<class item>& itemRepository::listItem() {
    static list<class item> res;
    res.clear();
    loopMap(items,v,class item) {
        res.push_back(*v);
    }
    res.sort();
    return res;
}
void itemRepository::delItem(const string v) { items.erase(v); }
const bool itemRepository::isItem(const string v) const { return items.count(v)>0; }
void itemRepository::save(class channel &ch) {
    loopItems(rec) {
        rec->save(ch);
    }
}

bool itemRepository::load(class channel &ch) {
    if (ch.key=="item") {
        ch.next();
        class item rec;
        rec.load(ch);
        setItem(rec);
        return true;
    }
    return false;
}
